/**
 * Copyright (c) 2009, Christian Schneider
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * - Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the names of the authors nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package name.cs.csutils;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class InvokationTarget implements ActionListener, Runnable {

    private static final Object[] NO_ARGS = new Object[0];
    private static final Class<?>[] NO_PARAMS = new Class<?>[0];
    
    private Object owner;
    private Method method;
    private String methodName;
    private Class<?>[] parameterTypes = NO_PARAMS; 
    private Object[] args = NO_ARGS;
    private boolean lookup = true;
    
    public InvokationTarget(Object owner, Method method) {
        this(owner);
        if (method == null) {
            throw new IllegalArgumentException("owner:"+owner+",method:"+method);
        }
        this.method = method;
    }

    public InvokationTarget(Object owner) {
        if (owner == null) {
            throw new IllegalArgumentException("owner:"+owner);
        }
        this.owner = owner;
    }

    public InvokationTarget(Object owner, String methodName, Class<?> ... parameterTypes) {
        this(owner);
        useMethod(methodName, parameterTypes);
    }
    
    public Method getMethod() {
        if (this.method == null && lookup) {
            lookup = false;
            Class<?> cls = getOwnerClass();
            Method m = null;
            try {
                m = cls.getMethod(methodName, parameterTypes);
            } catch (SecurityException ex) {
                // log: "could not lookup method:"+methodName, ex);
            } catch (NoSuchMethodException ex) {
             // log: "no such method:"+methodName,ex);
            }
            this.method = m;
        }
        return method;
    }
    
    private Class<?> getOwnerClass() {
        return owner instanceof Class<?> ? (Class<?>) owner : owner.getClass();
    }
    
    private Object getTargetObject() {
        return owner instanceof Class<?> ? null : owner;
    }

    public InvokationTarget useMethod(String methodName, Class<?> ... parameterTypes) {
        this.methodName = methodName;
        this.parameterTypes = parameterTypes;
        return this;
    }
    
    public InvokationTarget usingArgs(Object ... args) {
        this.args = args;
        return this;
    }
    
    public Object invoke() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        Method  m = getMethod();
        if (m == null) {
            throw new RuntimeException("invokation target undefined");
        }
        return m.invoke(getTargetObject(), args);
    }

    private void invokeQuiet() {
        try {
            invoke();
        } catch (Exception ex) {
            if (ex instanceof RuntimeException) {
                throw (RuntimeException)ex;
            } else {
                throw new RuntimeException(ex);
            }
        }
    }
    
    public void actionPerformed(ActionEvent e) {
        invokeQuiet();
    }

    public void run() {
        invokeQuiet();
    }
    
}
